package cn.kuporo.seckill.order.application.service.impl;

import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import cn.kuporo.seckill.common.cache.distribute.DistributedCacheService;
import cn.kuporo.seckill.common.constants.RedisConstants;
import cn.kuporo.seckill.common.exception.ErrorCode;
import cn.kuporo.seckill.common.exception.SeckillException;
import cn.kuporo.seckill.common.model.dto.SeckillOrderSubmitDTO;
import cn.kuporo.seckill.common.model.message.ErrorMessage;
import cn.kuporo.seckill.dubbo.interfaces.goods.SeckillGoodsDubboService;
import cn.kuporo.seckill.order.application.model.command.SeckillOrderCommand;
import cn.kuporo.seckill.order.application.model.command.SeckillOrderTaskCommand;
import cn.kuporo.seckill.order.application.place.SeckillPlaceOrderService;
import cn.kuporo.seckill.order.application.service.OrderTaskGenerateService;
import cn.kuporo.seckill.order.application.service.SeckillOrderService;
import cn.kuporo.seckill.order.domain.model.entity.SeckillOrder;
import cn.kuporo.seckill.order.domain.service.SeckillOrderDomainService;
import com.alibaba.fastjson.JSONObject;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Arrays;
import java.util.List;

/**
 * @author: kuporo
 * @since: 2025/7/7
 * @description:
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class SeckillOrderServiceImpl implements SeckillOrderService {
    private final SeckillOrderDomainService seckillOrderDomainService;
    private final SeckillPlaceOrderService seckillPlaceOrderService;
    private final DistributedCacheService distributedCacheService;
    private final OrderTaskGenerateService orderTaskGenerateService;
    @DubboReference
    private SeckillGoodsDubboService seckillGoodsDubboService;
    

    @Override
    @Transactional
    public Long saveSeckillOrder(SeckillOrderCommand seckillOrderCommand) {
        Long userId = 1L;
        if (seckillOrderCommand == null){
            throw new SeckillException(ErrorCode.PARAMS_INVALID);
        }
        return seckillPlaceOrderService.placeOrder(userId, seckillOrderCommand);


    }

    @Override
    public List<SeckillOrder> getSeckillOrderByUserId(Long userId) {
        return seckillOrderDomainService.getSeckillOrderByUserId(userId);
    }

    

    @Override
    @Transactional
    public void deleteOrder(ErrorMessage errorMessage) {
        Boolean submitTransaction = distributedCacheService.hasKey(RedisConstants.getKey(RedisConstants.ORDER_TX_KEY, errorMessage.getTxNo()));
        if(Boolean.FALSE.equals(submitTransaction)){
            log.info("deleteOrder|订单微服务未执行本地事务|{}",errorMessage.getTxNo());
            return;
        }
        seckillOrderDomainService.deleteOrderShardingUserId(errorMessage.getTxNo(), errorMessage.getUserId());
        seckillOrderDomainService.deleteOrderShardingGoodsId(errorMessage.getTxNo(), errorMessage.getGoodsId());
        // 清除掉幂等数据
        if (!StrUtil.isBlank(errorMessage.getOrderTaskId())){
            distributedCacheService.delete(RedisConstants.getKey(RedisConstants.ORDER_TASK_ORDER_ID_KEY,errorMessage.getOrderTaskId()));
            distributedCacheService.delete(RedisConstants.getKey(RedisConstants.ORDER_TASK_ID_KEY, errorMessage.getOrderTaskId()));
            
        }
        this.handlerCacheStock(errorMessage);
    }

    @Override
    public SeckillOrderSubmitDTO getSeckillOrderSubmitDTOByTaskId(SeckillOrderTaskCommand seckillOrderTaskCommand) {
        Long userId = 1L;
        String taskId = seckillOrderTaskCommand.getOrderTaskId();
        Long goodsId = seckillOrderTaskCommand.getGoodsId();
        String generateTaskId = orderTaskGenerateService.generatePlaceOrderTaskId(userId, goodsId);
        if (!generateTaskId.equals(taskId)){
            throw new SeckillException(ErrorCode.ORDER_TASK_ID_INVALID);
        }
        String key = RedisConstants.getKey(RedisConstants.ORDER_TASK_ORDER_ID_KEY, taskId);
        Long orderId = distributedCacheService.getObject(key, Long.class);
        if (orderId == null) {
            // 如果未获取到订单id，则再次返回任务id
            return new SeckillOrderSubmitDTO(taskId, goodsId, RedisConstants.TYPE_TASK);
        }
        // 返回订单id
        return new SeckillOrderSubmitDTO(String.valueOf(orderId), goodsId, RedisConstants.TYPE_ORDER);
    }

    /**
     * 处理缓存库存
     */
    private void handlerCacheStock(ErrorMessage errorMessage) {
        // 订单微服务之前未抛出异常，说明已经扣减了缓存中的库存，此时需要将缓存中的库存增加回来
        if (BooleanUtil.isFalse(errorMessage.getException())){
            String luaKey = RedisConstants.getKey(RedisConstants.ORDER_TX_KEY, errorMessage.getTxNo()).concat(RedisConstants.LUA_SUFFIX);
            Long result = distributedCacheService.checkExecute(luaKey, RedisConstants.ORDER_TX_LOG_EXPIRE_SECONDS);
            // 已经执行过恢复缓存库存的方法
            if (RedisConstants.CHECK_RECOVER_STOCK_HAS_EXECUTE.equals(result)){
                log.info("handlerCacheStock|已经执行过恢复缓存库存的方法|{}", JSONObject.toJSONString(errorMessage));
                return;
            }
            //分布式锁方式
            log.info("handlerCacheStock|回滚缓存库存|{}", JSONObject.toJSONString(errorMessage));
            switch (errorMessage.getPlaceOrderType()){
                case LOCK, LUA -> this.rollbackLockOrLuaStock(errorMessage);
                case BUCKET -> this.rollbackBucketStock(errorMessage);
            }
        }

    }

    /**
     * 回滚bucket模式的库存
     */
    private void rollbackBucketStock(ErrorMessage errorMessage) {
        // 获取库存分桶数据key
        String stockBucketKey = this.getStockBucketKey(errorMessage.getGoodsId(), errorMessage.getBucketSerialNo());
        // 获取库存编排时加锁的Key
        String stockBucketSuspendKey = this.getStockBucketSuspendKey(errorMessage.getGoodsId());
        // 获取库存校对key
        String stockBucketAlignKey = this.getStockBucketAlignKey(errorMessage.getGoodsId());
        // 封装执行Lua脚本的Key
        List<String> keys = Arrays.asList(stockBucketKey, stockBucketSuspendKey, stockBucketAlignKey);
        Long incrementResult = distributedCacheService.incrementBucketStock(keys, errorMessage.getQuantity());
        if (incrementResult == null || incrementResult != RedisConstants.LUA_BUCKET_STOCK_EXECUTE_SUCCESS){
            log.error("rollbackBucketStock|恢复预扣减的库存失败|{}", JSONUtil.toJsonStr(errorMessage));
        }
    }

    /**
     * 回滚Lock和Lua模式的库存
     */
    private void rollbackLockOrLuaStock(ErrorMessage errorMessage) {
        //只有分布式锁方式和Lua脚本方法才会扣减缓存中的库存
        String key = RedisConstants.getKey(RedisConstants.GOODS_ITEM_STOCK_KEY_PREFIX, String.valueOf(errorMessage.getGoodsId()));
        //分布式锁方式
        log.info("handlerCacheStock|回滚缓存库存|{}", JSONObject.toJSONString(errorMessage));
        switch (errorMessage.getPlaceOrderType()){
            case LOCK -> distributedCacheService.increment(key, errorMessage.getQuantity());
            case LUA -> distributedCacheService.incrementByLua(key, errorMessage.getQuantity());
        }
    }

    /**
     * 获取库存校对key
     */
    private String getStockBucketAlignKey(Long goodsId){
        return RedisConstants.getKey(RedisConstants.GOODS_STOCK_BUCKETS_ALIGN_KEY, goodsId);
    }

    /**
     * 获取库存编排时加锁的Key
     */
    private String getStockBucketSuspendKey(Long goodsId){
        return RedisConstants.getKey(RedisConstants.GOODS_STOCK_BUCKETS_SUSPEND_KEY, goodsId);
    }

    /**
     * 获取库存分桶数据Key
     */
    private String getStockBucketKey(Long goodsId, Integer serialNo){
        return RedisConstants.getKey(RedisConstants.getKey(RedisConstants.GOODS_BUCKET_AVAILABLE_STOCKS_KEY, goodsId), serialNo);
    }
}
